// Copyright 2019 The rhyme_lph Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// N [author] rhyme_lph
// T [email]  rhymelph@gmail.com
// Z [github] https://github.com/rhymelph

import 'dart:math' as math;

import 'package:base_plugin/base_plugin.dart';
import 'package:finance_app/generated/l10n.dart';
import 'package:finance_app/src/common/config.dart';
import 'package:finance_app/src/entities/coa_entity.dart';
import 'package:finance_app/src/entities/journal_account_amount.dart';
import 'package:finance_app/src/entities/journal_entry_detail_entity.dart';
import 'package:finance_app/src/entities/ocr_attachment_entity.dart';
import 'package:finance_app/src/entities/setting_data_entity.dart';
import 'package:finance_app/src/http_service/http_coa_service.dart';
import 'package:finance_app/src/mixin/from_attach_mixin.dart';
import 'package:finance_app/src/mixin/init_edit_data_mixin.dart';
import 'package:finance_app/src/router_gen/router_gen_providers.dart';
import 'package:finance_app/src/utils/date_time_utils.dart';
import 'package:finance_app/src/widgets/bottom_sheet/calendar_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/currency_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/delete_info_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/got_it_tip_bottom_sheet.dart';
import 'package:flutter/material.dart';

import '../date_report_provider.dart';

class NewJournalEntryProvider extends BaseProvider<JournalEntryDetailEntity>
    with InitEditDataMixin, FormAttachMixin {
  DateTime date;

  SettingDataCurrency currency;

  double get debitAmount {
    double amount = 0.0;
    debitList.forEach((element) {
      amount += element.total;
    });
    return amount;
  }

  List<JournalAccountAmount> debitList = [];
  List<JournalAccountAmount> creditList = [];

  double get creditAmount {
    double amount = 0.0;
    creditList.forEach((element) {
      amount += element.total;
    });
    return amount;
  }

  double get unbalancedAmount {
    return debitAmount - creditAmount;
  }

  bool get showMustEqual => unbalancedAmount != 0;

  TextEditingController memoController = TextEditingController();
  FocusNode memoFocusNode = FocusNode();

  TextEditingController journalNumberController = TextEditingController();
  FocusNode journalFocusNode = FocusNode();

  List<String> menuList = [];

  @override
  void dispose() {
    super.dispose();
    memoController.dispose();
    memoFocusNode.dispose();
    journalNumberController.dispose();
    journalFocusNode.dispose();
  }

  unfocus() {
    memoFocusNode.unfocus();
    journalFocusNode.unfocus();
  }

  @override
  Future get dataFuture => id == null ? null : HttpCoaService.showJournal(id);

  void onChangeDate() {
    obtainContext?.call((context) async {
      date = await showCalendarBottomSheet(context, date);
      update();
    });
  }

  void onChangeCurrency() {
    obtainContext?.call((context) async {
      currency = await showCurrencyBottomSheet(context, currency);
      update();
    });
  }

  void onAddDebitAccount() async {
    final result =
        await Routes.navigateTo(RouterGenProviders.accountsSelectPage);
    if (result != null) {
      debitList.add(result);
      update();
    }
  }

  void onAddCreditAccount() async {
    final result =
        await Routes.navigateTo(RouterGenProviders.accountsSelectPage);
    if (result != null) {
      creditList.add(result);
      update();
    }
  }

  void onSelectMenu(String value) {
    unfocus();
    switch (value) {
      case 'Edit':
        onChangeEditMode();
        break;
      case 'Delete':
        onDelete();
        break;
    }
  }

  void onSave() async {
    if (unbalancedAmount > 0) {
      update();
      return;
    }

    if (journalNumberController.text.length == 0) {
      showToast('Pleae enter Journal Entry Number.');
      return;
    }

    String dateString = DateTimeUtils.formatDateTime(date, 'yyyy-MM-dd');

    String memo = memoController.text;
    List journalDetail = [];

    for (final item in debitList) {
      final finder = journalDetail.firstWhere(
          (element) =>
              element['coa_account_detail_id'] == item.account.accountCode,
          orElse: () => null);
      if (finder == null) {
        journalDetail.add({
          'coa_account_detail_id': item.account.accountCode,
          'dr_amount': item.amount,
        });
      } else {
        finder['dr_amount'] = item.amount;
      }
    }

    for (final item in creditList) {
      final finder = journalDetail.firstWhere(
          (element) =>
              element['coa_account_detail_id'] == item.account.accountCode,
          orElse: () => null);
      if (finder == null) {
        journalDetail.add({
          'coa_account_detail_id': item.account.accountCode,
          'cr_amount': item.amount,
        });
      } else {
        finder['cr_amount'] = item.amount;
      }
    }

    for (final item in journalDetail) {
      if (item['cr_amount'] == null) {
        item['cr_amount'] = 0.0;
      }
      if (item['dr_amount'] == null) {
        item['dr_amount'] = 0.0;
      }
    }
    JournalEntryDetailEntity orderEntity;

    List<int> attachmentIds = await uploadFiles();
    if (attachmentIds == null) return;

    if (id == null) {
      await post(
          HttpCoaService.createJournal(
            memo: memo,
            currency: currency?.key,
            date: dateString,
            number: journalNumberController.text,
            journalDetail: journalDetail,
            amount: math.max(debitAmount, creditAmount),
            attachment_ids: attachmentIds,
          ),
          autoHandler: AutoHandler.Toast,
          loadingTip: S.current.loading, onSuccess: (result) {
        orderEntity = getEntityFromResult<JournalEntryDetailEntity>(result);
      });
    } else {
      await post(
          HttpCoaService.updateJournal(
            id,
            memo: memo,
            currency: currency?.key,
            date: dateString,
            number: journalNumberController.text,
            journalDetail: journalDetail,
            amount: math.max(debitAmount, creditAmount),
            attachment_ids: attachmentIds,
          ),
          autoHandler: AutoHandler.Toast,
          loadingTip: S.current.loading, onSuccess: (result) {
        orderEntity = getEntityFromResult<JournalEntryDetailEntity>(result);
      });
    }
    obtainContext?.call((context) {
      Providers.get<DateReportProvider>(RouterGenProviders.dateReportPage, null)
          ?.onTry();
      Navigator.of(context).pop();
    });
  }

  void onSelectDebit(JournalAccountAmount value) async {
    final result =
        await Routes.navigateTo(RouterGenProviders.accountAmountPage, params: {
      'account': value.account,
      'amount': value.amount,
      'gst': value.gst,
      'isCheckInclusive': value.isCheckInclusive
    });
    if (result != null && result is bool) {
      debitList.remove(value);
      update();
    } else if (result != null && result is JournalAccountAmount) {
      value
        ..amount = result.amount
        ..account = result.account
        ..gst = result.gst
        ..isCheckInclusive = result.isCheckInclusive;
      update();
    }
  }

  void onSelectCredit(JournalAccountAmount value) async {
    final result =
        await Routes.navigateTo(RouterGenProviders.accountAmountPage, params: {
      'account': value.account,
      'amount': value.amount,
      'gst': value.gst,
      'isCheckInclusive': value.isCheckInclusive
    });
    if (result != null && result is bool) {
      creditList.remove(value);
      update();
    } else if (result != null && result is JournalAccountAmount) {
      value
        ..amount = result.amount
        ..account = result.account
        ..gst = result.gst
        ..isCheckInclusive = result.isCheckInclusive;
      update();
    }
  }

  @override
  Future get editDataFuture => dataFuture;

  @override
  String get type => 'journal_entry';

  @override
  TextEditingController get numberCtl => journalNumberController;

  void onDelete() async {
    obtainContext?.call((context) async {
      bool isSuccess = await showDeleteJournalEntryInfoBottomSheet(context);
      if (isSuccess) {
        isSuccess = false;
        await post(HttpCoaService.deleteJournal(id),
            autoHandler: AutoHandler.Toast,
            loadingTip: 'Loading....', onSuccess: (result) {
          isSuccess = true;
        });
        if (isSuccess) {
          await showJournalEntryDeleteSuccessBottomSheet(context);
          Providers.get<DateReportProvider>(
                  RouterGenProviders.dateReportPage, null)
              .onTry();
          Navigator.of(context).pop();
        }
      }
    });
  }

  @override
  FutureOr<JournalEntryDetailEntity> transformData(
      JournalEntryDetailEntity d, bool isCache) {
    date = DateTimeUtils.toDateTime(d.date, 'yyyy-MM-dd HH:mm:ss');
    currency = Config.settingData.currency
        .firstWhere((element) => element.key == d.currency, orElse: () => null);
    memoController.text = d.memo ?? '';
    journalNumberController.text = d.refNumber;

    if (data.addFrom != null && data.addFrom == 'user_add') {
      menuList.add('Delete');
    }

    for (final item in d.journalDetail) {
      if (item.crAmount != 0) {
        creditList.add(JournalAccountAmount()
          ..account = (CoaCoaAccountHeaderCoaAccountDetail()
            ..accountCode = item.id.toString()
            ..name = item.coaAccountDetailName)
          ..amount = item.crAmount.toDouble());
      }
      if (item.drAmount != 0) {
        debitList.add(JournalAccountAmount()
          ..account = (CoaCoaAccountHeaderCoaAccountDetail()
            ..accountCode = item.id.toString()
            ..name = item.coaAccountDetailName)
          ..amount = item.drAmount.toDouble());
      }
    }
    return super.transformData(d, isCache);
  }

  @override
  void initState() {
    super.initState();
  }

  @override
  List<OcrAttachmentEntity> get attachments => data?.attachmentsInfo;

  @override
  void unFocus() {
    FocusManager.instance.primaryFocus.unfocus();
  }
}
